#include "cell.h"
/*
 * setdata()将数据放入“单元格”   输入
 * data()  从“单元格”中取出数据  显示
 *
 * 每个QTableWidgetItem都可以保存一些数据，最多可以为每一个数据角色分配一个QVariant变量
 *  Qt::DisplayRole 用于需要显示的数据上，单元格的值（对公式求值后的结果）
 *  Qt::EditRole    用于需要编辑的角色，  单元格公式
 *
 * ？？？单元格中一切皆字符串,在QT中就用QString类型, 然后根据你单元格中的内容进行转换,
*/
Cell::Cell()
{
    setDirty();
}
QTableWidgetItem *Cell::clone() const
{
  return new Cell(*this);
}
QVariant Cell::data(int role) const
{
    if(role==Qt::DisplayRole)
    {
       if(value().isValid())
       {
            return value().toString();
       }else{
           return "####";
       }
    }else if(role==Qt::TextAlignmentRole){
        if(value().type()==QVariant::String)
        {
            return int(Qt::AlignLeft|Qt::AlignVCenter);
        }else{
            return int(Qt::AlignRight|Qt::AlignCenter);
        }
    }else{
        return QTableWidgetItem::data(role);
    }
}
const QVariant Invalid;
QVariant Cell::value()const
{
   if(cacheIsDirty)
   {
        QString formulaStr=formula();     //所有的输入都当作“公式”
        if(formulaStr.startsWith('\''))
        {
            cacheValue=formulaStr.mid(1);
        }else if(formulaStr.startsWith('=')){
            cacheValue=Invalid;
            QString expr=formulaStr.mid(1);
            expr.replace("","");
            expr.append(QChar::Null);
            int pos=0;
            cacheValue=evalExpression(expr,pos);
            if(expr[pos]!=QChar::Null)
                cacheValue=Invalid;
        }else{
            bool ok;
            double d=formulaStr.toDouble(&ok);
            if(ok)
            {
                cacheValue=d;
            }else{
                cacheValue=formulaStr;
            }

        }
        cacheIsDirty=false;
   }
   return cacheValue;
}
QString Cell::formula()const
{
    return data(Qt::EditRole).toString();
}
void Cell::setFormula(const QString &formula)
{
    setData(Qt::EditRole,formula);
}

void Cell::setData(int role, const QVariant &value)
{
    QTableWidgetItem::setData(role,value);
    if(role==Qt::EditRole)
        setDirty();
}
void Cell::setDirty()
{
    cacheIsDirty=true;
}
QVariant Cell::evalExpression(const QString &str, int &pos) const
{
    QVariant result = evalTerm(str, pos);
    while (str[pos] != QChar::Null) {
        QChar op = str[pos];
        if (op != '+' && op != '-')
            return result;
        ++pos;

        QVariant term = evalTerm(str, pos);
        if (result.type() == QVariant::Double
                && term.type() == QVariant::Double) {
            if (op == '+') {
                result = result.toDouble() + term.toDouble();
            } else {
                result = result.toDouble() - term.toDouble();
            }
        } else {
            result = Invalid;
        }
    }
    return result;
}

QVariant Cell::evalTerm(const QString &str, int &pos) const
{
    QVariant result = evalFactor(str, pos);
    while (str[pos] != QChar::Null) {
        QChar op = str[pos];
        if (op != '*' && op != '/')
            return result;
        ++pos;

        QVariant factor = evalFactor(str, pos);
        if (result.type() == QVariant::Double
                && factor.type() == QVariant::Double) {
            if (op == '*') {
                result = result.toDouble() * factor.toDouble();
            } else {
                if (factor.toDouble() == 0.0) {
                    result = Invalid;
                } else {
                    result = result.toDouble() / factor.toDouble();
                }
            }
        } else {
            result = Invalid;
        }
    }
    return result;
}

QVariant Cell::evalFactor(const QString &str, int &pos) const
{
    QVariant result;
    bool negative = false;

    if (str[pos] == '-') {
        negative = true;
        ++pos;
    }

    if (str[pos] == '(') {
        ++pos;
        result = evalExpression(str, pos);
        if (str[pos] != ')')
            result = Invalid;
        ++pos;
    } else {
        QRegExp regExp("[A-Za-z][1-9][0-9]{0,2}");
        QString token;

        while (str[pos].isLetterOrNumber() || str[pos] == '.') {
            token += str[pos];
            ++pos;
        }

        if (regExp.exactMatch(token)) {
            int column = token[0].toUpper().unicode() - 'A';
            int row = token.mid(1).toInt() - 1;

            Cell *c = static_cast<Cell *>(
                              tableWidget()->item(row, column));
            if (c) {
                result = c->value();
            } else {
                result = 0.0;
            }
        } else {
            bool ok;
            result = token.toDouble(&ok);
            if (!ok)
                result = Invalid;
        }
    }

    if (negative) {
        if (result.type() == QVariant::Double) {
            result = -result.toDouble();
        } else {
            result = Invalid;
        }
    }
    return result;
}
